package com.douding.file.controller.admin;

import com.douding.server.domain.Test;
import com.douding.server.dto.FileDto;
import com.douding.server.dto.ResponseDto;
import com.douding.server.enums.FileUseEnum;
import com.douding.server.exception.BusinessException;
import com.douding.server.exception.BusinessExceptionCode;
import com.douding.server.exception.ValidatorException;
import com.douding.server.service.FileService;
import com.douding.server.service.TestService;
import com.douding.server.util.Base64ToMultipartFile;
import com.douding.server.util.UuidUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.util.List;

/*
    返回json 应用@RestController
    返回页面  用用@Controller
 */
@RequestMapping("/admin/file")
@RestController
public class UploadController {

    private static final Logger LOG = LoggerFactory.getLogger(UploadController.class);
    public  static final String BUSINESS_NAME ="文件上传";
    @Resource
    private TestService testService;

    @Value("${file.path}")
    private String FILE_PATH;

    @Value("${file.domain}")
    private String FILE_DOMAIN;

    @Resource
    private FileService fileService;

    @RequestMapping("/upload")
    public ResponseDto upload(@RequestBody FileDto fileDto) throws Exception {

        ResponseDto responseDto = new ResponseDto();

        //检验文件对象数据,参数是否完整
        if (checkFormat(fileDto)){
            //将base64转MultipartFile
            MultipartFile multipartFile = Base64ToMultipartFile.base64ToMultipart(fileDto.getShard());
            //文件储存目录，位置配置在douding-config的resources/config/dev/api-file-dev.yml进行配置
            LOG.info("User :{}",fileDto.getUse());
            //若use是T，则放到teacher文件目录，否则放到course文件目录
            String localDirPath = FILE_PATH + ("T".equals(fileDto.getUse())?"teacher":"course");
            LOG.info("文件储存目录 :{}",localDirPath);
            File dirFile = new File(localDirPath);
            //判断文件是否存在
            if (!dirFile.exists() && !dirFile.mkdirs()) {
                throw new Exception("文件夹创建失败，创建路径:" + localDirPath);
            }
            //本地文件全路径
            String fileFullPath = localDirPath +
                    File.separator +
                    fileDto.getKey() +
                    "." +
                    fileDto.getSuffix();
            LOG.info("本地文件全路径: {}",fileFullPath);
            //创建文件分片全路径，将multipartFile写入到这个路径中
            String fileShardFullPath = fileFullPath +
                    "." +
                    fileDto.getShardIndex();


            //此处必须关流，否则后面将无法进行删除分片文件
            multipartFile.transferTo(new File(fileShardFullPath));
            //若之前已插入分片，则更形否则插入

            String relaPath = ("T".equals(fileDto.getUse())?"teacher":"course") + "/" + fileDto.getKey() + "." + fileDto.getSuffix();
            fileDto.setPath(relaPath);
            fileService.save(fileDto);

            //判断当前分片索引是否等于分片总数，如果等于分片总数则执行文件合并
            if (fileDto.getShardIndex().equals(fileDto.getShardTotal())) {
                fileDto.setPath(fileFullPath);
                //文件合并
                merge(fileDto);
                //删除分片
                deleteShard(fileDto);
            }
            FileDto result = new FileDto();
            //设置文件映射地址给前端
            result.setPath(FILE_DOMAIN + "/" + relaPath);
            responseDto.setContent(result);
            return responseDto;
        }
        //缺失数据
        responseDto.setSuccess(false);
        return responseDto;
    }

    boolean checkFormat(FileDto fileDto){
//        name:文件名称(包含文件后缀)。
//        shard:文件base64值。
//        size:文件总大小。
//        shardTotal:文件总分片数。
//        shardSize:每个分片最大值，注意这里笔者说的分片最大值，原因很简单，如果我们文件分片大小。为10M，很可能最后一个分片大小不足10M，所以这个参数传的就是切割分片后的最大值。
//        suffix:文件后缀，视频后缀为mp4,图片则为jpg等。
        if (fileDto.getName()==null){
            return false;
        }
        if (fileDto.getShard()==null){
            return false;
        }
        if (fileDto.getSize()==null){
            return false;
        }
        if (fileDto.getShardTotal()==null){
            return false;
        }
        if (fileDto.getShardSize()==null){
            return false;
        }
        if (fileDto.getSuffix()==null){
            return false;
        }
        if (fileDto.getUse()==null){
            return false;
        }
        return true;
    }

    //合并分片
    public void merge(FileDto fileDto) throws Exception {
        LOG.info("合并分片开始");
        String path = fileDto.getPath();
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(path, true);
            for (Integer i = 1; i <= fileDto.getShardTotal(); i++) {
                FileInputStream inputStream = null;
                try  {
                    inputStream = new FileInputStream(path + "." + i);
                    //定义字符缓冲区
                    byte[] bytes = new byte[10 * 1024 * 1024];
                    int len;
                    while ((len = inputStream.read(bytes)) != -1) {
                        outputStream.write(bytes, 0, len);
                    }
                }
                //关流
                catch (Exception e){
                    LOG.error(e.getMessage());
                }
                if (inputStream!=null){
                    inputStream.close();
                }
            }
        }
        catch (Exception e) {
            LOG.error("文件合并失败:{}", e.getMessage(), e);
        }
        finally {
            if (outputStream!=null){
                outputStream.close();
            }
        }
    }

    //删除分片
    public void deleteShard(FileDto fileDto){
        String path = fileDto.getPath();
        //删除所有分片
        for (Integer i = 1; i <= fileDto.getShardTotal(); i++) {
            File file = new File(path + "." + i);
            boolean flag = file.getAbsoluteFile().delete();
        }

    }

    @GetMapping("/check/{key}")
    public ResponseDto check(@PathVariable String key) throws Exception {
        LOG.info("检查上传分片开始：{}", key);
        if (key == null || "".equals(key)) {
            throw new ValidatorException("格式不符合");
        }

        FileDto fileDto = fileService.findByKey(key);
        ResponseDto responseDto = new ResponseDto();

        //如果不为空，则返回映射地址以及文件上传进度
        if (fileDto != null) {
            //将文件映射地址告知前端
            fileDto.setPath(FILE_DOMAIN + "/" + fileDto.getPath());
            responseDto.setContent(fileDto);
        }

        return responseDto;
    }

}//end class
